﻿using System;
using System.Collections.Generic;
using System.Linq;
using Algoritmia.DataStructures.Digraphs;

namespace Demos.Spanning_Trees
{
    public enum Elemento
    {
        Vertice,
        Arista
    } ;

    public class Vertice:IEquatable<Vertice>
    {
        public double X { get; private set; }
        public double Y { get; private set; }

        public Vertice(double x, double y)
        {
            X = x;
            Y = y;
        }

        public bool Equals(Vertice other)
        {
            return X == other.X && Y == other.Y;
        }
    }

    public class Model
    {
        public delegate void CrearElemento(Elemento elem, List<double> valores, string etiqueta, int indice);
        public event CrearElemento NuevoElemento;

        private readonly Random _r;

        public Digraph<Vertice> Grafo { get; private set; }
        public WeightingFunction<Vertice, double> WeightingFunction { get; private set; }
        public List<Vertice> Vertices { get; private set; }
        public List<Tuple<Vertice, Vertice>> Aristas { get; private set; }
        public Dictionary<Tuple<Vertice, Vertice>, double> Pesos { get; private set; }
        public Dictionary<Object, string> Etiquetas { get; set; }
        private readonly int _numVertices;
        private readonly int _maxAristas;
        private readonly double _maxX;
        private readonly double _maxY; 
        private readonly double _minX;
        private readonly double _minY;

        public Vertice Origen { get; set; }
        public Vertice Destino { get; set; }
        public int AristaSeleccionada { get; set; }

        public Model(int numVertices, int maxAristas, double maxX, double maxY, double minX, double minY)
        {
            Grafo = new Digraph<Vertice>(null, new List<Tuple<Vertice, Vertice>>());
            WeightingFunction = null;
            Vertices = new List<Vertice>();
            Aristas = new List<Tuple<Vertice, Vertice>>();
            Pesos = new Dictionary<Tuple<Vertice, Vertice>, double>();
            Etiquetas = new Dictionary<object, string>();
            _r = new Random(DateTime.Now.Millisecond);
            _numVertices = numVertices;
            _maxAristas = maxAristas;
            _maxX = maxX;
            _maxY = maxY;
            _minX = minX;
            _minY = minY;
            AristaSeleccionada = -1;
        }

        public void NuevoGrafoYWeightingFunction()
        {
            var vertices = new List<Vertice>();
            var aristas = new List<Tuple<Vertice, Vertice>>();
            Etiquetas = new Dictionary<object, string>();

            for (var i = 0; i < _numVertices; i++)
            {
                vertices.Add(new Vertice(_r.NextDouble()*_maxX + _minX, _r.NextDouble()*_maxY + _minY));
                Etiquetas[vertices.Last()] = i.ToString();
            }

            foreach (var vertice in vertices)
            {
                var numAristas = _r.Next(1, _maxAristas+1);
                for (var i = 0; i < numAristas; i++)
                {
                    var vertice2 = vertices[_r.Next(0, vertices.Count)];
                    if (vertice != vertice2)
                    {
                        aristas.Add(new Tuple<Vertice, Vertice>(vertice, vertice2));
                        Etiquetas[aristas.Last()] = "";
                    }
                }
            }

            Grafo = new Digraph<Vertice>(vertices, aristas);
            WeightingFunction = NuevaWightingFunction(aristas);
            Vertices = vertices;
            Aristas = aristas;
        }

        private WeightingFunction<Vertice, double> NuevaWightingFunction(IEnumerable<Tuple<Vertice, Vertice>> aristas)
        {
            Pesos = new Dictionary<Tuple<Vertice, Vertice>, double>();
            var secuencia = aristas.Select(arista => new KeyValuePair<Tuple<Vertice, Vertice>, double>(arista, Math.Sqrt(Math.Pow(arista.Item2.X - arista.Item1.X, 2) + Math.Pow(arista.Item2.Y - arista.Item1.Y, 2)))).ToList();
            foreach (var par in secuencia)
            {
                Pesos[par.Key] = par.Value;
                Etiquetas[par.Key] = String.Format("{0,1:F1}", par.Value);
            }
            return Algoritmia.DataStructures.Digraphs.WeightingFunction.CreateFromSequence(secuencia);
        }

        public void RefrescarGrafoYWeightingFunction()
        {
            Grafo = new Digraph<Vertice>(Vertices, Aristas);
            WeightingFunction = NuevaWightingFunction(Aristas);
        }

        public void CrearArbol()
        {
            int indice = 0;
            foreach (var arista in Aristas)
            {
                if (NuevoElemento != null)
                {
                    NuevoElemento(Elemento.Arista,
                                  new List<double> {arista.Item1.X, arista.Item1.Y, arista.Item2.X, arista.Item2.Y},
                                  Etiquetas[arista], indice);
                }
                indice++;
            }

            indice = 0;
            foreach (var vertex in Vertices)
            {
                if (NuevoElemento != null)
                {
                    NuevoElemento(Elemento.Vertice, new List<double> { vertex.X, vertex.Y }, Etiquetas[vertex], indice);
                }
                indice++;
            }
        }

        public void AñadirVertice(double x, double y)
        {
            Vertices.Add(new Vertice(x, y));
            Etiquetas[Vertices.Last()] = (Vertices.Count - 1).ToString();
            RefrescarGrafoYWeightingFunction();
            if (NuevoElemento != null)
            {
                NuevoElemento(
                    Elemento.Vertice, new List<double> 
                    { Vertices.Last().X, Vertices.Last().Y }, Etiquetas[Vertices.Last()], Vertices.Count-1);
            }
        }

        public void AñadirArista()
        {
            if (Origen != null && Destino != null)
            {
                Aristas.Add(new Tuple<Vertice, Vertice>(Origen, Destino));
                Etiquetas[Aristas.Last()] = "";
                RefrescarGrafoYWeightingFunction();
                NuevoElemento(
                    Elemento.Arista, new List<double> 
                    { Aristas.Last().Item1.X, Aristas.Last().Item1.Y, Aristas.Last().Item2.X, Aristas.Last().Item2.Y }, 
                    Etiquetas[Aristas.Last()], Aristas.Count-1);
            }
        }

        public void EliminarArista()
        {
            Aristas.RemoveAt(AristaSeleccionada);
            RefrescarGrafoYWeightingFunction();
        }

        public void EliminarVertice()
        {
            var aristasSobrantes = new List<Tuple<Vertice, Vertice>>();

            if (Origen != null)
            {
                aristasSobrantes.AddRange(Aristas.Where(arista => arista.Item1 == Origen || arista.Item2 == Origen));
                Vertices.Remove(Origen);
            }
            if (Destino != null)
            {
                aristasSobrantes.AddRange(Aristas.Where(arista => arista.Item1 == Destino || arista.Item2 == Destino));
                Vertices.Remove(Destino);
            }

            foreach (var aristaSobrante in aristasSobrantes)
            {
                Aristas.Remove(aristaSobrante);
            }

            RefrescarGrafoYWeightingFunction();
        }

        public void BorrarArbol()
        {
            Vertices.Clear();
            Aristas.Clear();
        }

        public void NuevoGrafoPrefabricado()
        {
            var vertices = new List<Vertice>();
            var aristas = new List<Tuple<Vertice, Vertice>>();
            Etiquetas = new Dictionary<object, string>();

            vertices.Add(new Vertice(20, 20));
            Etiquetas[vertices.Last()] = "1";
            vertices.Add(new Vertice(20, 100));
            Etiquetas[vertices.Last()] = "2";
            vertices.Add(new Vertice(20, 200));
            Etiquetas[vertices.Last()] = "3";
            vertices.Add(new Vertice(120, 20));
            Etiquetas[vertices.Last()] = "4";
            vertices.Add(new Vertice(120, 100));
            Etiquetas[vertices.Last()] = "5";
            vertices.Add(new Vertice(120, 200));
            Etiquetas[vertices.Last()] = "6";
            vertices.Add(new Vertice(220, 100));
            Etiquetas[vertices.Last()] = "7";
            vertices.Add(new Vertice(150, 280));
            Etiquetas[vertices.Last()] = "8";
            vertices.Add(new Vertice(300, 40));
            Etiquetas[vertices.Last()] = "9";
            vertices.Add(new Vertice(280, 160));
            Etiquetas[vertices.Last()] = "10";
            vertices.Add(new Vertice(400, 250));
            Etiquetas[vertices.Last()] = "11";
            vertices.Add(new Vertice(450, 120));
            Etiquetas[vertices.Last()] = "12";

            aristas.Add(new Tuple<Vertice, Vertice>(vertices[0], vertices[1]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[1], vertices[2]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[1], vertices[3]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[3], vertices[4]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[4], vertices[5]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[5], vertices[6]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[6], vertices[7]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[2], vertices[7]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[7], vertices[8]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[8], vertices[9]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[10], vertices[11]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[8], vertices[11]));
            Etiquetas[aristas.Last()] = "";
            aristas.Add(new Tuple<Vertice, Vertice>(vertices[9], vertices[11]));
            Etiquetas[aristas.Last()] = "";

            Grafo = new Digraph<Vertice>(vertices, aristas);
            WeightingFunction = NuevaWightingFunction(aristas);
            Vertices = vertices;
            Aristas = aristas;
        }
    }
}
